package com.dq.controller;

import com.dq.controller.client.Md5Object;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * @author zhangzeli
 */
@RestController
@RequestMapping(value = "/System")
public class SystemController {

    private static final Logger LOGGER = LoggerFactory.getLogger(SystemController.class);

    /**
     * 保护的质量，这个参数规定服务器支持哪种保护方案.
     * 客户端可以从列表中选择一个值
     * “auth”表示只进行身份查验，
     * “auth-int”表示进行查验外，还有一些完整性保护。
     * 需要看更详细的描述，请参阅RFC2617；
     **/
    public static final String QOP = "auth,auth-int";
    public static final String AUTHINT = "auth-int";
    public static final String AUTH = "auth";

    public static final String MD5SESS = "MD5-sess";

    private static final String COLON ="\"";

    /**
     * 一个不透明的（不让外人知道其意义）数据字符串，这些数据请求使用同一保护空间内的url。
     * 服务器产生的由客户下去请求时原样返回。最好是Base64串或十六进制字符串
     **/
    private String opaque = "bHhWSTU2RjFMakZ0c3lTcGJF";

    /**
     * 显示给用户的字符串，这样用户就知道可以用那个用户名和密码了。
     * 至少包含主机名，还可能包含用户集合
     **/
    private String realm = "zhangzeli@host.com";

    private String digest = "Digest";

    /**
     * 用户名
     **/
   // private String userName = "zhangzeli";

    /**
     * 密码
     **/
    private String upassword = "123456";

    @RequestMapping(value = "/Register",method = RequestMethod.POST)
    public Object register(@RequestHeader(value = "Authorization", required = false) String authorization, @RequestBody(required = false) String body) {
        LOGGER.info("请求body"+body);
        if (authorization != null && authorization.startsWith(digest)) {
            String auth = authorization.substring(7, authorization.length());
            Map<String, String> mapByKeyArray = getMapByKeyArray(auth.split(","));
            //用户名
            String cusername = mapByKeyArray.get("username");
            //认证域
            String realm = mapByKeyArray.get("realm");
            //服务端返回唯一字符串
            String nonce = mapByKeyArray.get("nonce");
            //加密方式  MD5   MD5-sess;客户端没传递默认MD5
            String algorithm = mapByKeyArray.get("algorithm");
            //客户端请求url
            String uri = mapByKeyArray.get("uri");
            String qop = mapByKeyArray.get("qop");
            //客户端计数器
            String nc = mapByKeyArray.get("nc");
            //客户端生成唯一字符串
            String cnonce = mapByKeyArray.get("cnonce");
            //客户端按规则加密后的字符串
            String cresponse = mapByKeyArray.get("response");
            //服务端返回的随机字符串
            String opaque = mapByKeyArray.get("opaque");

            //计算HA1  区分MD5和MD5-sess加密方式
            //algorithm=MD5  HA1=MD5(username:realm:password)
            //algorithm=MD5-sess HA1=MD5(username:realm:password:nonce:cnonce)
            String ha1 = "";
            if (MD5SESS.equals(algorithm)) {
                ha1 = Md5Object.encrypt(Md5Object.encrypt(cusername + ":" + realm + ":" + upassword )+":" +nonce + ":" + cnonce);
            } else {
                ha1 = Md5Object.encrypt(cusername + ":" + realm + ":" + upassword);
            }
            LOGGER.info("HA1：[" + cusername + ":" + realm + ":" + upassword + "]-->[" + ha1 + "]");
            //计算HA2
            //qop=auth或未指定 HA2=MD5(method:uri)
            //qop=auth-int   HA2=MD5(method:uri:MD5(requestbody))
            String ha2 = "";
            if (AUTHINT.equals(qop)) {
                if (StringUtils.isEmpty(body)) {
                    body = Md5Object.encrypt("");
                } else {
                    body = Md5Object.encrypt(body);
                }
                ha2 = Md5Object.encrypt("POST:"+uri+":"+ body);
                LOGGER.info("HA2：[" + "POST:" + uri + ":" + body + "]-->[" + ha2 + "]");
            } else {
                ha2 = Md5Object.encrypt("POST:"+uri);
                LOGGER.info("HA2：[" + "POST:" + uri + "]-->[" + ha2 + "]");
            }

            //计算response
            //qop=auth或auth-int  response=MD5(ha1:nonce:nc:cnonce:qop:ha2)
            //qop未指定 response=MD5(ha1:nonce:ha2)
            String response = "";
            if (AUTH.equals(qop) || AUTHINT.equals(qop)) {
                response = Md5Object.encrypt(ha1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + ha2);
                LOGGER.info("sresponse:[" + ha1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + ha2 + "]-->[" + response + "]");
            } else {
                response = Md5Object.encrypt(ha1 + ":" + nonce + ":" + ha2);
                LOGGER.info("sresponse:[" + ha1 + ":" + nonce + ":" + ha2 + "]-->[" + response + "]");
            }

            //设置相应体内容
            //响应体对象
            Map<String, Object> responseBody = new HashMap<>(8);
            if (response.equalsIgnoreCase(cresponse)) {
                responseBody.put("code", "success");
                responseBody.put("message", "验证通过");
                return new ResponseEntity<Map<String, Object>>(responseBody, HttpStatus.OK);
            }
            LOGGER.info("认证不通过");
        }
        LOGGER.info("没有请求头信息或不是digest认证请求，服务端进行授权");
        //随机生成
        String nonce = Md5Object.uuid();
        StringBuffer sb = new StringBuffer();
        sb.append("realm=\"").append(realm).append("\",")
                .append("qop=\"").append(QOP).append("\",")
                .append("nonce=\"").append(nonce).append("\",")
                .append("opaque=\"").append(opaque).append("\"");
        //设置相应头内容
        //响应头对象
        HttpHeaders headers = new HttpHeaders();
        headers.add("WWW-Authorization", digest + " " + sb.toString());
        return new ResponseEntity<Void>(headers, HttpStatus.UNAUTHORIZED);
    }

    public static Map<String, String> getMapByKeyArray(String[] resourceStr) {
        Map<String, String> maps = new HashMap<String, String>(8);
        LOGGER.info("***********" + Arrays.toString(resourceStr));
        try {
            Arrays.asList(resourceStr).stream().forEach(ele ->{
                String value =ele.split("=")[1].trim();
                if(value.startsWith(COLON)&&value.endsWith(COLON)){
                    maps.put(ele.split("=")[0].trim(),value.substring(1,value.length()-1));
                }else{
                    maps.put(ele.split("=")[0].trim(), value);
                }
                }
            );
            maps.forEach((k, v) -> LOGGER.info(k + "--" + v));
        }catch (Exception e){
            LOGGER.info("数据转换异常",e);
        }
        return maps;
    }

}